package org.wx.io.netty.chart.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @description: server Handler
 * @author: wangxun
 * @createDate: 2021/2/18
 * @version: 1.0
 */
@Slf4j
public class ChartServerHandler extends ChannelInboundHandlerAdapter {
    static final String HEART_BEAT = "PING";
    static final String HEART_BEAT_ECHO = "PONG";
    /**
     * 通道集合
     */
    static Set<ChannelHandlerContext> contextSet = new HashSet<>();

    /**
     * 用户连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        String msg  = "用户"+ctx.channel().remoteAddress()+"上线";
        log.info("用户{}上线",ctx.channel().remoteAddress());
        // 添加渠道
        contextSet.add(ctx);
        // 广播
        broadcast(msg,ctx);
    }

    /**
     * 用户消息送达
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        String contentStr = buf.toString(Charset.forName("utf-8"));

        if(HEART_BEAT.equals(contentStr)){
            ctx.writeAndFlush(Unpooled.copiedBuffer(HEART_BEAT_ECHO,Charset.forName("utf-8")));
            return;
        }


        log.info("用户:{} 说:{}",ctx.channel().remoteAddress(), contentStr);
        String content = ctx.channel().remoteAddress()+" 说:"+ contentStr;
        // 广播
        broadcast(content,ctx);
    }

    /**
     * 用户通道取消
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        // 退出，提出
        contextSet.remove(ctx);
        String content = "用户"+ctx.channel().remoteAddress()+"下线";
        log.info(content);
        broadcast(content,ctx);
    }


    /**
     * 用户通道发生异常
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("exceptionCaught"+cause.toString());
    }

    /**
     * 空闲事件检测
     * 由上一个IdleStateHandler传递过来
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent) evt;
            switch (event.state()){
                case READER_IDLE:
                    //log.info("{} 读空闲",ctx.channel().remoteAddress());
                    break;
                case WRITER_IDLE:
                    //log.info("{} 写空闲",ctx.channel().remoteAddress());
                    break;
                case ALL_IDLE:
                    //log.info("{} 读写空闲",ctx.channel().remoteAddress());
                    break;
            }

        }
    }

    /**
     * 广播消息 不包含自己
     * @param msg 消息
     * @param owner 发送者
     */
    void broadcast(String msg,ChannelHandlerContext owner){

        Iterator<ChannelHandlerContext> iterator = contextSet.iterator();
        while (iterator.hasNext()){


            ChannelHandlerContext context = iterator.next();
            if(context == owner){ // 剔除发送者
                continue;
            }
            /*
               如果想使用一个bytebuf 那么就得把引用次数重置
             */

            context.channel().writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
        }
    }
}
